Class MikleSocket 'by VirtualMikle
#Region "Declarations and State property"
'sockets tcp/ip
Private tcpClient As Net.Sockets.TcpClient, tcpListener As Net.Sockets.TcpListener, NetStream As Net.Sockets.NetworkStream
'multi-threading listening, thread for new data ,thread for disconnection checking
Private TListen As Threading.Thread, TData As Threading.Thread, TCheck As Threading.Thread
'used to close an output connection request, socket status
Private ConnectAsync As IAsyncResult, _Condition As Status = Status.Disconnected
'events
Public Event Connected As EventHandler
Public Event ConnectionLost As Eventhandler
Public Event ConnectionRequest As System.ComponentModel.CancelEventHandler
Public Event DataArrival(ByVal Data() As Byte)
'socket's statuses
Public Enum Status
Connected
Disconnected
ConnectionRequest
Listening
Connecting
End Enum
'return the current socket's status
Public ReadOnly Property Condition As Status
Get
Return _Condition
End Get
End Property
#End Region
#Region "Connect"
Public Sub BeginConnect(ByVal Host As String, ByVal Port As UInt32)
tcpClient = New Net.Sockets.TcpClient
'start the operation on another thread and save the IAsyncResult so we'll be able to kill this request
_Condition = Status.Connecting
ConnectAsync = tcpClient.BeginConnect(Host, Port, AddressOf ThreadConnected, Nothing)
End Sub
Private Sub ThreadConnected()
'get packets stream
NetStream = tcpClient.GetStream
'set the status and destroy the ConnectAsync
_Condition = Status.Connected
ConnectAsync = Nothing
'start checking status
TCheck = New Threading.Thread(AddressOf CheckStatus) : TCheck.Start()
SafeEvent(ConnectedEvent, {Me, New EventArgs})
End Sub
#End Region
#Region "Listen"
Public Sub BeginListen(ByVal Port As UInt32)
TListen = New Threading.Thread(AddressOf ThreadListen)
'get the local ip and set the listener to the specified port
tcpListener = New Net.Sockets.TcpListener(Net.IPAddress.Parse("127.0.0.1"), Port)
'start listening and set the socket status
tcpListener.Start()
_Condition = Status.Listening
'start the aynchronous operation
TListen.Start()
End Sub
'asynchronous operation
Private Sub ThreadListen()
Dim Cancel As New System.ComponentModel.CancelEventArgs
'just wait until there's a connection request
Do Until tcpListener.Pending
Loop
'raise the connection request event in a thread-safe way
SafeEvent(ConnectionRequestEvent, {Me, Cancel})
'check if the connection has not been rejected
If Not Cancel.Cancel Then
'accept the connection and get objects
tcpClient = tcpListener.AcceptTcpClient()
tcpListener.Stop()
NetStream = tcpClient.GetStream
_Condition = Status.Connected
'start checking status
TCheck = New Threading.Thread(AddressOf CheckStatus) : TCheck.Start()
'raise safely the connection completed event
SafeEvent(ConnectedEvent, {Me, New EventArgs})
End If
End Sub
#End Region
#Region "Data and CheckStatus"
Private Sub GetData()
'check if there's new data
If tcpClient.Available > 0 Then
Dim Buffer(tcpClient.Available - 1) As Byte
'get the new data
NetStream.Read(Buffer, 0, Buffer.Length)
'thread safe dataarrival event
SafeEvent(DataArrivalEvent, {Buffer})
End If
'start a thread to see if the socket is still connected
TCheck = New Threading.Thread(AddressOf CheckStatus) : TCheck.Start()
End Sub
'write the byte data (send a packet)
Public Sub SendData(ByVal Data() As Byte)
'if it not connected then throw the right exception
If _Condition = Status.Connected Then
NetStream.Write(Data, 0, Data.Length)
Else : Throw New Net.Sockets.SocketException(10057)
End If
End Sub
'overloads with string data
Public Sub SendData(ByVal Data As String)
If _Condition = Status.Connected Then
Dim Buffer() As Byte = System.Text.UTF8Encoding.UTF8.GetBytes(Data)
NetStream.Write(Buffer, 0, Buffer.Length)
Else : Throw New Net.Sockets.SocketException(10057)
End If
End Sub
'check if the socket is still connected
Private Sub CheckStatus()
If tcpClient.Available = 0 AndAlso tcpClient.Client.Poll(0, Net.Sockets.SelectMode.SelectRead) AndAlso tcpListener Is Nothing Then
'close all apart from this thread
Close()
'thread safe connectionlost event
SafeEvent(ConnectionLostEvent, {Me, New EventArgs})
'close this thread
Exit Sub
End If
'begin checking for new data
TData = New Threading.Thread(AddressOf GetData) : TData.Start()
End Sub
#End Region
#Region "Close and SafeEvent"
'close all
Public Sub Close()
On Error Resume Next
'abort listening thread
If TListen IsNot Nothing Then TListen.Abort()
'end tcpclient connection request
If ConnectAsync IsNot Nothing Then
tcpClient.EndConnect(ConnectAsync)
ConnectAsync = Nothing
End If
'abort checking-data thread
If TData IsNot Nothing Then TData.Abort()
'close listener
If tcpListener IsNot Nothing Then tcpListener.Server.Close()
'close client
If tcpClient IsNot Nothing Then tcpClient.Close()
'close networkstream
If NetStream IsNot Nothing Then NetStream.Close()
_Condition = Status.Disconnected
End Sub
'thread safe raiseevent
Private Sub SafeEvent(ByVal MyEvent As [Delegate], ByVal Parameters() As Object)
If MyEvent IsNot Nothing Then
Dim CurObj As Object
'get all the delegates
For Each MyDel As [Delegate] In MyEvent.GetInvocationList
CurObj = MyDel.Target
CurObj.BeginInvoke(MyDel, Parameters)
Next
End If
End Sub
#End Region
End Class